﻿// YamlScannerTest.cs
//
// Copyright 2008 by Burt Harris
// Licensed under the Microsoft Public License (Ms-PL)
// See: http://www.codeplex.com/yaml/license
//

using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using Microsoft.Scripting;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YaTools.Yaml.UnitTests
{
    public class YamlScannerTest : YamlScanner
    {
        #region Additional test attributes

        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //

        #endregion

        private TestContext testContextInstance;

        public TestContext TestContext
        {
            get { return testContextInstance; }
            set { testContextInstance = value; }
        }

        protected void YamlTest(string yaml, params object[] expects)
        {
            Contract.Requires(yaml != null);

            SetSource(yaml);

            Trace.WriteLine("YamlTest (raw)");
            // YamlScanner.TraceRawTokens.Level = TraceLevel.Info;
            // YamlScanner.TraceStateChanges.Level = TraceLevel.Info;

            int i = ShowInput(yaml);

            NextToken();

            for (i = 0; i < expects.Length; i++)
            {
                xn:
                object expected = expects[i];
                var expectedString = expected as string;

                if (expectedString != null && base.TokenText == expectedString)
                {
                    NextToken();
                    continue;
                }

                if (expected is Token && ((Token) expected) == Token)
                {
                }
                else if (Token >= Token.NoOp)
                {
                    NextToken();
                    goto xn;
                }
                else if (expected is string)
                    Expect(expected, base.TokenText);
                else
                    Expect(expected, Token);
                NextToken();
            }
            while (Token != Token.EndOfStream)
            {
                if (Token < Token.NoOp)
                    Expect(Token.EndOfStream, Token);
                NextToken();
            }
        }

        private int ShowInput(string yaml)
        {
            Contract.Requires(yaml != null);

            DebugLine("Test '{0}'", testContextInstance.TestName);
            DebugLine("<<INPUT>>");

            int i;
            using (var reader = new StringReader(yaml))
            {
                for (i = 1;; i++) // Show line numbers
                {
                    string line = reader.ReadLine();
                    if (line == null)
                        break;
                    DebugLine("{0:0000}: {1}", i, DebugDisplay(line));
                }
                ;
            }

            DebugLine("<<OUTPUT>>");
            base.SetSource(yaml);
            return i;
        }

        protected void InvalidYamlTest(string yaml)
        {
            SetSource(yaml);
            Token t;
            do
            {
                t = NextToken();
            } while (t != Token.EndOfStream);
        }

        private void Expect(object expected, object value)
        {
            if (!expected.Equals(value))
            {
                if (expected is Token && Token.PlainEnd == (Token) expected
                    && value is Token && Token.Outdent == (Token) value)
                    return;

                var expectedString = expected as string;
                if (expectedString != null)
                    expectedString = DebugDisplay((string) expected);
                else
                    expectedString = expected.ToString();

                SourceLocation location = TokenStart;

                DebugLine("<<EXPECTED>> {0}", expectedString);
                DebugLine("<<DEBUG>>{0}", base.DebugLookahead);
                DebugLine("<<FOLLOWING>>\r\n");
                for (int i = 0; i < 10 && Token != Token.EndOfStream; i++)
                    NextToken();
                if (expected is string && value is string)
                {
                    if ((string) expected != (string) value)
                        throw new AssertFailedException(string.Format(
                            "at {0}, Expected:<{1}>, Actual:<{2}>",
                            location, DebugDisplay((string) expected),
                            DebugDisplay((string) value)));
                }
                else
                {
                    if (!expected.Equals(value))
                        throw new AssertFailedException(string.Format(
                            "at {0}, expected {1}, actual {2}",
                            location, expected, value));
                }
            }
        }

        [Conditional("DEBUG")]
        protected void DebugLine(string format, params object[] args)
        {
            Contract.Requires(!string.IsNullOrEmpty(format));
            Contract.Requires(args != null);

            Debug.WriteLine(string.Format(format, args));
        }
    }
}